package client

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/google/uuid"
)

type EOP struct {
	AK string
	SK string
}

func NewEOP(ak string, sk string) EOP {
	return EOP{
		AK: ak,
		SK: sk,
	}
}

func (eop EOP) hmacSHA256(secret, data []byte) []byte {
	h := hmac.New(sha256.New, secret)
	h.Write(data)
	return h.Sum(nil)
}

func (eop EOP) base64OfHMAC(data []byte) string {
	return base64.StdEncoding.EncodeToString(data)
}

func (eop EOP) getSortedStr(data map[string]interface{}) string {
	var keys []string
	for k := range data {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	var strList []string
	for _, k := range keys {
		strList = append(strList, fmt.Sprintf("%s=%s", k, data[k]))
	}
	return strings.Join(strList, "&")
}

func (eop EOP) buildSign(queryParams map[string]interface{}, bodyParams interface{}, eopDate, requestUUID string) string {
	var bodybys []byte
	if bodyParams != nil {
		if bp, ok := bodyParams.([]byte); ok {
			bodybys = bp
		} else {
			bodybys, _ = json.Marshal(bodyParams)
		}
	}
	bodyDigest := sha256.Sum256(bodybys)

	headerStr := fmt.Sprintf("ctyun-eop-request-id:%s\neop-date:%s\n", requestUUID, eopDate)
	queryStr := eop.getSortedStr(queryParams)
	signatureStr := fmt.Sprintf("%s\n%s\n%x", headerStr, queryStr, bodyDigest)

	signDate := strings.Split(eopDate, "T")[0]

	kTime := eop.hmacSHA256([]byte(eop.SK), []byte(eopDate))
	kAK := eop.hmacSHA256(kTime, []byte(eop.AK))
	kDate := eop.hmacSHA256(kAK, []byte(signDate))
	signatureBase64 := eop.base64OfHMAC(eop.hmacSHA256(kDate, []byte(signatureStr)))

	signHeader := fmt.Sprintf("%s Headers=ctyun-eop-request-id;eop-date Signature=%s", eop.AK, signatureBase64)
	return signHeader
}

func (eop EOP) GetSignHeaders(queryParams map[string]interface{}, body interface{}) http.Header {
	localtion, _ := time.LoadLocation("Asia/Shanghai")
	now := time.Now().In(localtion)
	eopDate := now.Format("20060102T150405Z")
	requestUUID := uuid.New().String()
	headers := http.Header{}
	headers.Set("eop-date", eopDate)
	headers.Set("ctyun-eop-request-id", requestUUID)
	headers.Set("Eop-Authorization", eop.buildSign(queryParams, body, eopDate, requestUUID))
	return headers
}
